package com.iteaj.iot;

import com.iteaj.iot.client.ClientComponent;
import com.iteaj.iot.config.ConnectProperties;
import com.iteaj.iot.server.IotSocketServer;
import com.iteaj.iot.server.ServerComponent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.stream.Collectors;

public class DefaultComponentFactory implements ComponentFactory {

    private Logger logger = LoggerFactory.getLogger(getClass());
    private Map<String, FrameworkComponent> portMaps = new HashMap<>();
    private static ComponentFactory componentFactory = new DefaultComponentFactory();
    private Map<Class<? extends Message>, FrameworkComponent> messageComponentMap = new HashMap(8);

    protected DefaultComponentFactory() { }

    public static ComponentFactory getInstance() {
        return componentFactory;
    }

    @Override
    public int size() {
        return messageComponentMap.size();
    }

    @Override
    public FrameworkComponent get(Class<? extends Message> messageClazz) {
        return messageComponentMap.get(messageClazz);
    }

    @Override
    public List<ClientComponent> clients() {
        return Collections.unmodifiableList(messageComponentMap.values().stream()
                .filter(item -> item instanceof ClientComponent)
                .map(item -> (ClientComponent)item)
                .collect(Collectors.toList()));
    }

    @Override
    public List<ServerComponent> servers() {
        return Collections.unmodifiableList(messageComponentMap.values().stream()
                .filter(item -> item instanceof ServerComponent)
                .map(item -> (ServerComponent)item)
                .collect(Collectors.toList()));
    }

    @Override
    public FrameworkComponent get(PortType type, Integer port) {
        return portMaps.get(type.name() + ":" + port.toString());
    }

    @Override
    public synchronized void start(Class<?> messageClazz) {
        FrameworkComponent component = messageComponentMap.get(messageClazz);
        if(component != null) {
            component.start(null);
            registerToTimeoutManager(component);
        }
    }

    protected void registerToTimeoutManager(FrameworkComponent component) {
        if(component instanceof SocketProtocolFactory) {
            ProtocolTimeoutStorage delegation = ((SocketProtocolFactory<?>) component).getDelegation();
            FrameworkManager.getInstance().getTimeoutManager().register(delegation);
        }
    }

    @Override
    public synchronized boolean stop(Class<?> messageClazz) {
        FrameworkComponent component = messageComponentMap.get(messageClazz);
        if(component != null) {
            component.close();
            unregisterFromTimeoutManager(component);
            return true;
        }

        return false;
    }

    @Override
    public synchronized boolean close(Class<?> messageClazz) {
        FrameworkComponent component = messageComponentMap.get(messageClazz);
        if(component != null) {
            try {
                component.close();
                messageComponentMap.remove(messageClazz);

                if(component instanceof IotSocketServer) {
                    ConnectProperties config = ((IotSocketServer) component).config();
                    portMaps.remove(((IotSocketServer) component).getPortType().name() + ":" + config.getPort());
                }

                unregisterFromTimeoutManager(component);

                return true;
            } catch (Exception e) {
                logger.error("关闭组件失败", e);
            }
        }

        return false;
    }

    protected void unregisterFromTimeoutManager(FrameworkComponent component) {
        if (component instanceof SocketProtocolFactory) {
            ProtocolTimeoutStorage delegation = ((SocketProtocolFactory<?>) component).getDelegation();
            FrameworkManager.getInstance().getTimeoutManager().remove(delegation);
        }
    }

    @Override
    public synchronized void register(FrameworkComponent component) {
        if(component instanceof IotSocketServer) {
            ConnectProperties config = ((IotSocketServer) component).config();
            if(config == null || config.getPort() == null) {
                throw new FrameworkException("未指定端口配置");
            }

            PortType portType = ((IotSocketServer) component).getPortType();
            FrameworkComponent frameworkComponent = this.get(portType, config.getPort());
            // 已经有相同的端口
            if(frameworkComponent != null) {
                throw new FrameworkException("组件[" + frameworkComponent.getName()
                        + "]和[" + component.getName() + "]端口冲突: " + config.getPort());
            }

            if(config.getPort() <= 0 || config.getPort() > 65535)
                throw new FrameworkException("服务端组件(" + component.getName() + ")使用错误的端口: " + config.getPort());

            portMaps.put(portType.name() + ":" + config.getPort(), component);
        }

        Class<? extends Message> messageClass = component.getMessageClass();
        if(messageClass == null) {
            throw new FrameworkException("未声明组件的报文类型 "
                    + component.getClass().getSimpleName()+"<M extend ServerMessage>");
        }

        if(get(messageClass) == null) {
            messageComponentMap.put(messageClass, component);
        }
    }

    @Override
    public void start(Object config) {
        Collection<FrameworkComponent> components = messageComponentMap.values();

        // 先启用所有服务端组件
        components.stream()
                .filter(item -> item instanceof ServerComponent)
                .forEach(component -> {
                    component.start(config);
                    registerToTimeoutManager(component);
                });

        // 再启用客户端组件
        components.stream()
                .filter(item -> item instanceof ClientComponent)
                .forEach(component -> {
                    component.start(config);
                    registerToTimeoutManager(component);
                });
    }

    @Override
    public void close() {
        // 停止所有组件
        messageComponentMap.values()
                .forEach(clientComponent -> clientComponent.close());

        // 移除所有组件
        portMaps.clear();
        messageComponentMap.clear();
    }
}
